package com.fjsh.aop;

import com.fjsh.anno.Duplicate;
import cn.hutool.core.lang.Assert;
import com.fjsh.utils.HttpServletRequestUtil;
import com.fjsh.utils.RedissonUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * 防重复提交
 *
 * TODO: 1.消除魔法值 2根据自身微服务定义不同的防重方式，思路：通过spring.application.name + token + 路径 + 参数来标识不同服务
 */
@Aspect
@Component
public class DuplicateAop {

    @Pointcut("@annotation(com.fjsh.anno.Duplicate)")
    public void doAspect() {}

    @Around("doAspect()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        HttpServletRequest request = HttpServletRequestUtil.getRequest();
        Assert.notNull(request,"request can not null");

        Class<?> classTarget=pjp.getTarget().getClass();
        //获取方法签名(通过此签名获取目标方法信息)
        MethodSignature ms = (MethodSignature)pjp.getSignature();
        Method method = classTarget.getDeclaredMethod(ms.getName(), ms.getParameterTypes());
        //获取到注解
        Duplicate duplicate = method.getAnnotation(Duplicate.class);

        String token = request.getHeader("token");
        String path = request.getServletPath();

        String key = getKey(token,path);

        boolean lock = RedissonUtil.tryLock(key, TimeUnit.MILLISECONDS, duplicate.waitTime(), duplicate.leaseTime());
        if (lock){
            try {
                return pjp.proceed();
            } finally {
                RedissonUtil.unlock(key);
            }
        }
        throw new RuntimeException("重复请求,请稍后再试");
    }

    /**
     * 设置key
     * @param token
     * @param path
     * @return
     */
    private String getKey(String token, String path) {
        return token + path;
    }
}
